package com.kaifamiao.begins;

import java.util.Arrays;
import java.util.Objects;

public class Bag {

    // 约定默认初始容量为 10
    private static final int DEFAULT_INITIAL_CAPACITY = 10;
    // 约定默认加载因子
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 指向用来存放元素的数组实例
    private Object[] elements;
    // 用来记录有效元素个数的实例变量
    private int count;
    // 用来记录加载因子的实例变量
    private float factor;

    // 在无参构造中初始化 elements 和 factor 变量
    public Bag() {
        this.elements = new Object[ DEFAULT_INITIAL_CAPACITY ];
        this.factor = DEFAULT_LOAD_FACTOR;
    }

    public Bag(int initialCapacity, float loadFactor){
        if( initialCapacity < 0 ) {
            throw new IllegalArgumentException("初始容量不能小于零");
        }
        if( loadFactor <= 0 || loadFactor >= 1 ) {
            throw new IllegalArgumentException("加载因子必须是0到1之间的浮点数");
        }
        this.elements = new Object[ initialCapacity ];
        this.factor = loadFactor;
    }

    /**
     * 将参数对应的元素添加到 `Bag` 中
     * @param e 被添加的元素
     */
    public void add( Object e ) {
        ensureCapacity();
        // 在内部实现上，其实是将 参数对应的对象 添加到 数组中
        elements[count] = e ;
        count++;
    }

    private void ensureCapacity(){
        // 判断是否到达扩容条件
        if( count >= elements.length * factor ) {
            // 先将原数组用临时变量缓存起来
            Object[] temp = elements;
            // oldCapacity * 3 / 2 + 1
            elements = new Object[ temp.length * 3 / 2 + 1 ];
            // 把原数组中 [0, count) 之间的元素拷贝到新数组中
            System.arraycopy(temp,0,elements,0,count);
        }
    }

    // 返回`Bag`中元素个数
    public int size(){
        return count;
    }

    // 返回`Bag`容量
    public int capacity(){
        return elements.length;
    }

    // 判断`Bag`是否是空的
    public boolean isEmpty(){
        return count == 0;
    }

    // 清空`Bag`
    public void clear(){
        // 使用 第四个参数 填充 elements 数组 [ 0, count ) 之间的各个位置
        Arrays.fill( elements, 0 , count , null );
        // 将元素个数修改为零
        count = 0;
    }

    public Object[] toArray(){
        Object[] array = new Object[count];
        System.arraycopy( elements,0 , array, 0 , count);
        return array;
        //return Arrays.copyOfRange( elements, 0 , count );
    }

    /**
     * 判断参数表示的元素在`Bag`中是否存在
     * @param o 被检查的元素
     * @return 仅当被检查元素在`Bag`中存在时返回true
     */
    public boolean contains( Object o ) {
        return indexOf(o) != -1 ;
    }

    /**
     * 在`Bag`中寻找参数所表示的元素首次出现的位置
     * @param o 被查找的元素
     * @return 当被查找元素存在时返回其索引值，否则返回 -1
     */
    public int indexOf( Object o ){
        for (int i = 0; i < count; i++) {
            Object x = elements[i];
            // if( x == o || ( x != null && x.equals(o) ) ) {
            if( Objects.equals( x , o ) ) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 在`Bag`中寻找参数所表示的元素最后一次出现的位置
     * @param o 被查找的元素
     * @return 当被查找元素存在时返回其索引值，否则返回 -1
     */
    public int lastIndexOf( Object o ){
        for (int i = count -1 ; i >= 0; i--) {
            Object x = elements[i];
            if( Objects.equals( x , o ) ) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 从`Bag`中删除参数表示的元素
     * @param o 表示即将被删除的元素
     * @return 当删除成功时返回true，否则返回false
     */
    public boolean remove( Object o ) {
        int index = indexOf(o);
        if( index != -1 ) {
            int n = count - index - 1;
            // 将 elements 数组中 从 index + 1 开始的 n 个元素
            // 拷贝到 elements 数组的 index 处 (以此为起点存放n个元素)
            System.arraycopy(elements, index + 1 , elements, index, n );
            elements[ --count ] = null;
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append( "[" );
        for (int i = 0; i < count ; i++) {
            Object e = elements[i];
            builder.append( e );
            builder.append( ',' );
        }
        int last = builder.lastIndexOf(",");
        if( last != -1 ) {
            builder.deleteCharAt( last );
        }
        builder.append( "]" );
        String str = builder.toString();
        return str;
    }
}
